package org.moonlight.jvm.instructions;

import org.moonlight.jvm.instructions.base.Instruction;
import org.moonlight.jvm.instructions.comparisons.dcmp.DCmpG;
import org.moonlight.jvm.instructions.comparisons.dcmp.DCmpL;
import org.moonlight.jvm.instructions.comparisons.fcmp.FCmpG;
import org.moonlight.jvm.instructions.comparisons.fcmp.FCmpL;
import org.moonlight.jvm.instructions.comparisons.ifacmp.IfACmpEq;
import org.moonlight.jvm.instructions.comparisons.ifacmp.IfACmpNe;
import org.moonlight.jvm.instructions.comparisons.ifcond.*;
import org.moonlight.jvm.instructions.comparisons.ificmp.*;
import org.moonlight.jvm.instructions.comparisons.lcmp.LCmp;
import org.moonlight.jvm.instructions.constants.*;
import org.moonlight.jvm.instructions.constants.ldc.Ldc;
import org.moonlight.jvm.instructions.constants.ldc.Ldc2W;
import org.moonlight.jvm.instructions.constants.ldc.LdcW;
import org.moonlight.jvm.instructions.control.Goto;
import org.moonlight.jvm.instructions.control.ret.*;
import org.moonlight.jvm.instructions.control.switchcase.LookupSwitch;
import org.moonlight.jvm.instructions.control.switchcase.TableSwitch;
import org.moonlight.jvm.instructions.conversions.d2x.D2F;
import org.moonlight.jvm.instructions.conversions.d2x.D2I;
import org.moonlight.jvm.instructions.conversions.d2x.D2L;
import org.moonlight.jvm.instructions.conversions.f2x.F2D;
import org.moonlight.jvm.instructions.conversions.f2x.F2I;
import org.moonlight.jvm.instructions.conversions.f2x.F2L;
import org.moonlight.jvm.instructions.conversions.i2x.*;
import org.moonlight.jvm.instructions.conversions.l2x.L2D;
import org.moonlight.jvm.instructions.conversions.l2x.L2F;
import org.moonlight.jvm.instructions.conversions.l2x.L2I;
import org.moonlight.jvm.instructions.extended.GotoW;
import org.moonlight.jvm.instructions.extended.IfNonNull;
import org.moonlight.jvm.instructions.extended.IfNull;
import org.moonlight.jvm.instructions.extended.Wide;
import org.moonlight.jvm.instructions.loads.aload.*;
import org.moonlight.jvm.instructions.loads.dload.*;
import org.moonlight.jvm.instructions.loads.fload.*;
import org.moonlight.jvm.instructions.loads.iload.*;
import org.moonlight.jvm.instructions.loads.lload.*;
import org.moonlight.jvm.instructions.loads.xaload.*;
import org.moonlight.jvm.instructions.math.arithmetic.IInc;
import org.moonlight.jvm.instructions.math.arithmetic.add.DAdd;
import org.moonlight.jvm.instructions.math.arithmetic.add.FAdd;
import org.moonlight.jvm.instructions.math.arithmetic.add.IAdd;
import org.moonlight.jvm.instructions.math.arithmetic.add.LAdd;
import org.moonlight.jvm.instructions.math.arithmetic.div.DDiv;
import org.moonlight.jvm.instructions.math.arithmetic.div.FDiv;
import org.moonlight.jvm.instructions.math.arithmetic.div.IDiv;
import org.moonlight.jvm.instructions.math.arithmetic.div.LDiv;
import org.moonlight.jvm.instructions.math.arithmetic.mul.DMul;
import org.moonlight.jvm.instructions.math.arithmetic.mul.FMul;
import org.moonlight.jvm.instructions.math.arithmetic.mul.IMul;
import org.moonlight.jvm.instructions.math.arithmetic.mul.LMul;
import org.moonlight.jvm.instructions.math.arithmetic.neg.DNeg;
import org.moonlight.jvm.instructions.math.arithmetic.neg.FNeg;
import org.moonlight.jvm.instructions.math.arithmetic.neg.INeg;
import org.moonlight.jvm.instructions.math.arithmetic.neg.LNeg;
import org.moonlight.jvm.instructions.math.arithmetic.rem.DRem;
import org.moonlight.jvm.instructions.math.arithmetic.rem.FRem;
import org.moonlight.jvm.instructions.math.arithmetic.rem.IRem;
import org.moonlight.jvm.instructions.math.arithmetic.rem.LRem;
import org.moonlight.jvm.instructions.math.arithmetic.sub.DSub;
import org.moonlight.jvm.instructions.math.arithmetic.sub.FSub;
import org.moonlight.jvm.instructions.math.arithmetic.sub.ISub;
import org.moonlight.jvm.instructions.math.arithmetic.sub.LSub;
import org.moonlight.jvm.instructions.math.bitoperate.shift.*;
import org.moonlight.jvm.instructions.math.bool.and.IAnd;
import org.moonlight.jvm.instructions.math.bool.and.LAnd;
import org.moonlight.jvm.instructions.math.bool.or.IOr;
import org.moonlight.jvm.instructions.math.bool.or.LOr;
import org.moonlight.jvm.instructions.math.bool.xor.IXor;
import org.moonlight.jvm.instructions.math.bool.xor.LXor;
import org.moonlight.jvm.instructions.references.*;
import org.moonlight.jvm.instructions.references.array.ANewArray;
import org.moonlight.jvm.instructions.references.array.ArrayLength;
import org.moonlight.jvm.instructions.references.array.MultiANewArray;
import org.moonlight.jvm.instructions.references.array.NewArray;
import org.moonlight.jvm.instructions.references.invoke.InvokeInterface;
import org.moonlight.jvm.instructions.references.invoke.InvokeSpecial;
import org.moonlight.jvm.instructions.references.invoke.InvokeStatic;
import org.moonlight.jvm.instructions.references.invoke.InvokeVirtual;
import org.moonlight.jvm.instructions.reserved.InvokeNative;
import org.moonlight.jvm.instructions.stack.dup.*;
import org.moonlight.jvm.instructions.stack.pop.Pop;
import org.moonlight.jvm.instructions.stack.pop.Pop2;
import org.moonlight.jvm.instructions.stack.swap.Swap;
import org.moonlight.jvm.instructions.stores.astore.*;
import org.moonlight.jvm.instructions.stores.dstore.*;
import org.moonlight.jvm.instructions.stores.fstore.*;
import org.moonlight.jvm.instructions.stores.istore.*;
import org.moonlight.jvm.instructions.stores.lstore.*;
import org.moonlight.jvm.instructions.stores.xastore.*;

/**
 * 指令工厂 - 根据操作码创建具体的指令
 *
 * @author Moonlight
 * @createTime 2023/8/23 11:21
 **/
public class Factory {

    /**
     * 没有操作数的指令，不需要每次都创建一个实例，直接给单例就好了
     **/
    private static final Nop NOP = new Nop();
    private static final AConstNull A_CONST_NULL = new AConstNull();
    private static final IConstM1 I_CONST_M1 = new IConstM1();
    private static final IConst0 I_CONST0 = new IConst0();
    private static final IConst1 I_CONST1 = new IConst1();
    private static final IConst2 I_CONST2 = new IConst2();
    private static final IConst3 I_CONST3 = new IConst3();
    private static final IConst4 I_CONST4 = new IConst4();
    private static final IConst5 I_CONST5 = new IConst5();
    private static final LConst0 L_CONST0 = new LConst0();
    private static final LConst1 L_CONST1 = new LConst1();
    private static final FConst0 F_CONST0 = new FConst0();
    private static final FConst1 F_CONST1 = new FConst1();
    private static final FConst2 F_CONST2 = new FConst2();
    private static final DConst0 D_CONST0 = new DConst0();
    private static final DConst1 D_CONST1 = new DConst1();
    private static final ILoad0 I_LOAD0 = new ILoad0();
    private static final ILoad1 I_LOAD1 = new ILoad1();
    private static final ILoad2 I_LOAD2 = new ILoad2();
    private static final ILoad3 I_LOAD3 = new ILoad3();
    private static final LLoad0 L_LOAD0 = new LLoad0();
    private static final LLoad1 L_LOAD1 = new LLoad1();
    private static final LLoad2 L_LOAD2 = new LLoad2();
    private static final LLoad3 L_LOAD3 = new LLoad3();
    private static final FLoad0 F_LOAD0 = new FLoad0();
    private static final FLoad1 F_LOAD1 = new FLoad1();
    private static final FLoad2 F_LOAD2 = new FLoad2();
    private static final FLoad3 F_LOAD3 = new FLoad3();
    private static final DLoad0 D_LOAD0 = new DLoad0();
    private static final DLoad1 D_LOAD1 = new DLoad1();
    private static final DLoad2 D_LOAD2 = new DLoad2();
    private static final DLoad3 D_LOAD3 = new DLoad3();
    private static final ALoad0 A_LOAD0 = new ALoad0();
    private static final ALoad1 A_LOAD1 = new ALoad1();
    private static final ALoad2 A_LOAD2 = new ALoad2();
    private static final ALoad3 A_LOAD3 = new ALoad3();
    private static final IStore0 I_STORE0 = new IStore0();
    private static final IStore1 I_STORE1 = new IStore1();
    private static final IStore2 I_STORE2 = new IStore2();
    private static final IStore3 I_STORE3 = new IStore3();
    private static final LStore0 L_STORE0 = new LStore0();
    private static final LStore1 L_STORE1 = new LStore1();
    private static final LStore2 L_STORE2 = new LStore2();
    private static final LStore3 L_STORE3 = new LStore3();
    private static final FStore0 F_STORE0 = new FStore0();
    private static final FStore1 F_STORE1 = new FStore1();
    private static final FStore2 F_STORE2 = new FStore2();
    private static final FStore3 F_STORE3 = new FStore3();
    private static final DStore0 D_STORE0 = new DStore0();
    private static final DStore1 D_STORE1 = new DStore1();
    private static final DStore2 D_STORE2 = new DStore2();
    private static final DStore3 D_STORE3 = new DStore3();
    private static final AStore0 A_STORE0 = new AStore0();
    private static final AStore1 A_STORE1 = new AStore1();
    private static final AStore2 A_STORE2 = new AStore2();
    private static final AStore3 A_STORE3 = new AStore3();
    private static final Pop POP = new Pop();
    private static final Pop2 POP2 = new Pop2();
    private static final Dup DUP = new Dup();
    private static final DupX1 DUP_X1 = new DupX1();
    private static final DupX2 DUP_X2 = new DupX2();
    private static final Dup2 DUP2 = new Dup2();
    private static final Dup2X1 DUP2_X1 = new Dup2X1();
    private static final Dup2X2 DUP2_X2 = new Dup2X2();
    private static final Swap SWAP = new Swap();
    private static final IAdd I_ADD = new IAdd();
    private static final LAdd L_ADD = new LAdd();
    private static final FAdd F_ADD = new FAdd();
    private static final DAdd D_ADD = new DAdd();
    private static final ISub I_SUB = new ISub();
    private static final LSub L_SUB = new LSub();
    private static final FSub F_SUB = new FSub();
    private static final DSub D_SUB = new DSub();
    private static final IMul I_MUL = new IMul();
    private static final LMul L_MUL = new LMul();
    private static final FMul F_MUL = new FMul();
    private static final DMul D_MUL = new DMul();
    private static final IDiv I_DIV = new IDiv();
    private static final LDiv L_DIV = new LDiv();
    private static final FDiv F_DIV = new FDiv();
    private static final DDiv D_DIV = new DDiv();
    private static final IRem I_REM = new IRem();
    private static final LRem L_REM = new LRem();
    private static final FRem F_REM = new FRem();
    private static final DRem D_REM = new DRem();
    private static final INeg I_NEG = new INeg();
    private static final LNeg L_NEG = new LNeg();
    private static final FNeg F_NEG = new FNeg();
    private static final DNeg D_NEG = new DNeg();
    private static final IShl I_SHL = new IShl();
    private static final LShl L_SHL = new LShl();
    private static final IShr I_SHR = new IShr();
    private static final LShr L_SHR = new LShr();
    private static final IUShr I_U_SHR = new IUShr();
    private static final LUShr L_U_SHR = new LUShr();
    private static final IAnd I_AND = new IAnd();
    private static final LAnd L_AND = new LAnd();
    private static final IOr I_OR = new IOr();
    private static final LOr L_OR = new LOr();
    private static final IXor I_XOR = new IXor();
    private static final LXor L_XOR = new LXor();
    private static final I2B I_2_B = new I2B();
    private static final I2C I_2_C = new I2C();
    private static final I2S I_2_S = new I2S();
    private static final I2L I_2_L = new I2L();
    private static final I2F I_2_F = new I2F();
    private static final I2D I_2_D = new I2D();
    private static final L2I L_2_I = new L2I();
    private static final L2F L_2_F = new L2F();
    private static final L2D L_2_D = new L2D();
    private static final F2I F_2_I = new F2I();
    private static final F2L F_2_L = new F2L();
    private static final F2D F_2_D = new F2D();
    private static final D2I D_2_I = new D2I();
    private static final D2L D_2_L = new D2L();
    private static final D2F D_2_F = new D2F();
    private static final LCmp L_CMP = new LCmp();
    private static final FCmpL F_CMP_L = new FCmpL();
    private static final FCmpG F_CMP_G = new FCmpG();
    private static final DCmpL D_CMP_L = new DCmpL();
    private static final DCmpG D_CMP_G = new DCmpG();
    private static final Return RETURN = new Return();
    private static final AReturn A_RETURN = new AReturn();
    private static final DReturn D_RETURN = new DReturn();
    private static final FReturn F_RETURN = new FReturn();
    private static final IReturn I_RETURN = new IReturn();
    private static final LReturn L_RETURN = new LReturn();
    private static final AALoad AA_LOAD = new AALoad();
    private static final BALoad BA_LOAD = new BALoad();
    private static final CALoad CA_LOAD = new CALoad();
    private static final DALoad DA_LOAD = new DALoad();
    private static final FALoad FA_LOAD = new FALoad();
    private static final IALoad IA_LOAD = new IALoad();
    private static final LALoad LA_LOAD = new LALoad();
    private static final SALoad SA_LOAD = new SALoad();
    private static final AAStore AA_STORE = new AAStore();
    private static final BAStore BA_STORE = new BAStore();
    private static final CAStore CA_STORE = new CAStore();
    private static final DAStore DA_STORE = new DAStore();
    private static final FAStore FA_STORE = new FAStore();
    private static final IAStore IA_STORE = new IAStore();
    private static final LAStore LA_STORE = new LAStore();
    private static final SAStore SA_STORE = new SAStore();
    private static final InvokeNative INVOKE_NATIVE = new InvokeNative();
    private static final AThrow A_THROW = new AThrow();


    public static Instruction newInstruction(int opcode) {
        switch (opcode) {
            case 0x00:
                return NOP;
            case 0x01:
                return A_CONST_NULL;
            case 0x02:
                return I_CONST_M1;
            case 0x03:
                return I_CONST0;
            case 0x04:
                return I_CONST1;
            case 0x05:
                return I_CONST2;
            case 0x06:
                return I_CONST3;
            case 0x07:
                return I_CONST4;
            case 0x08:
                return I_CONST5;
            case 0x09:
                return L_CONST0;
            case 0x0a:
                return L_CONST1;
            case 0x0b:
                return F_CONST0;
            case 0x0c:
                return F_CONST1;
            case 0x0d:
                return F_CONST2;
            case 0x0e:
                return D_CONST0;
            case 0x0f:
                return D_CONST1;
            case 0x10:
                return new BiPush();
            case 0x11:
                return new SiPush();
            case 0x12:
                return new Ldc();
            case 0x13:
                return new LdcW();
            case 0x14:
                return new Ldc2W();
            case 0x15:
                return new ILoad();
            case 0x16:
                return new LLoad();
            case 0x17:
                return new FLoad();
            case 0x18:
                return new DLoad();
            case 0x19:
                return new ALoad();
            case 0x1a:
                return I_LOAD0;
            case 0x1b:
                return I_LOAD1;
            case 0x1c:
                return I_LOAD2;
            case 0x1d:
                return I_LOAD3;
            case 0x1e:
                return L_LOAD0;
            case 0x1f:
                return L_LOAD1;
            case 0x20:
                return L_LOAD2;
            case 0x21:
                return L_LOAD3;
            case 0x22:
                return F_LOAD0;
            case 0x23:
                return F_LOAD1;
            case 0x24:
                return F_LOAD2;
            case 0x25:
                return F_LOAD3;
            case 0x26:
                return D_LOAD0;
            case 0x27:
                return D_LOAD1;
            case 0x28:
                return D_LOAD2;
            case 0x29:
                return D_LOAD3;
            case 0x2a:
                return A_LOAD0;
            case 0x2b:
                return A_LOAD1;
            case 0x2c:
                return A_LOAD2;
            case 0x2d:
                return A_LOAD3;
             case 0x2e:
             	return IA_LOAD;
             case 0x2f:
             	return LA_LOAD;
             case 0x30:
             	return FA_LOAD;
             case 0x31:
             	return DA_LOAD;
             case 0x32:
             	return AA_LOAD;
             case 0x33:
             	return BA_LOAD;
             case 0x34:
             	return CA_LOAD;
             case 0x35:
             	return SA_LOAD;
            case 0x36:
                return new IStore();
            case 0x37:
                return new LStore();
            case 0x38:
                return new FStore();
            case 0x39:
                return new DStore();
            case 0x3a:
                return new AStore();
            case 0x3b:
                return I_STORE0;
            case 0x3c:
                return I_STORE1;
            case 0x3d:
                return I_STORE2;
            case 0x3e:
                return I_STORE3;
            case 0x3f:
                return L_STORE0;
            case 0x40:
                return L_STORE1;
            case 0x41:
                return L_STORE2;
            case 0x42:
                return L_STORE3;
            case 0x43:
                return F_STORE0;
            case 0x44:
                return F_STORE1;
            case 0x45:
                return F_STORE2;
            case 0x46:
                return F_STORE3;
            case 0x47:
                return D_STORE0;
            case 0x48:
                return D_STORE1;
            case 0x49:
                return D_STORE2;
            case 0x4a:
                return D_STORE3;
            case 0x4b:
                return A_STORE0;
            case 0x4c:
                return A_STORE1;
            case 0x4d:
                return A_STORE2;
            case 0x4e:
                return A_STORE3;
             case 0x4f:
             	return IA_STORE;
             case 0x50:
             	return LA_STORE;
             case 0x51:
             	return FA_STORE;
             case 0x52:
             	return DA_STORE;
             case 0x53:
             	return AA_STORE;
             case 0x54:
             	return BA_STORE;
             case 0x55:
             	return CA_STORE;
             case 0x56:
             	return SA_STORE;
            case 0x57:
                return POP;
            case 0x58:
                return POP2;
            case 0x59:
                return DUP;
            case 0x5a:
                return DUP_X1;
            case 0x5b:
                return DUP_X2;
            case 0x5c:
                return DUP2;
            case 0x5d:
                return DUP2_X1;
            case 0x5e:
                return DUP2_X2;
            case 0x5f:
                return SWAP;
            case 0x60:
                return I_ADD;
            case 0x61:
                return L_ADD;
            case 0x62:
                return F_ADD;
            case 0x63:
                return D_ADD;
            case 0x64:
                return I_SUB;
            case 0x65:
                return L_SUB;
            case 0x66:
                return F_SUB;
            case 0x67:
                return D_SUB;
            case 0x68:
                return I_MUL;
            case 0x69:
                return L_MUL;
            case 0x6a:
                return F_MUL;
            case 0x6b:
                return D_MUL;
            case 0x6c:
                return I_DIV;
            case 0x6d:
                return L_DIV;
            case 0x6e:
                return F_DIV;
            case 0x6f:
                return D_DIV;
            case 0x70:
                return I_REM;
            case 0x71:
                return L_REM;
            case 0x72:
                return F_REM;
            case 0x73:
                return D_REM;
            case 0x74:
                return I_NEG;
            case 0x75:
                return L_NEG;
            case 0x76:
                return F_NEG;
            case 0x77:
                return D_NEG;
            case 0x78:
                return I_SHL;
            case 0x79:
                return L_SHL;
            case 0x7a:
                return I_SHR;
            case 0x7b:
                return L_SHR;
            case 0x7c:
                return I_U_SHR;
            case 0x7d:
                return L_U_SHR;
            case 0x7e:
                return I_AND;
            case 0x7f:
                return L_AND;
            case 0x80:
                return I_OR;
            case 0x81:
                return L_OR;
            case 0x82:
                return I_XOR;
            case 0x83:
                return L_XOR;
            case 0x84:
                return new IInc();
            case 0x85:
                return I_2_L;
            case 0x86:
                return I_2_F;
            case 0x87:
                return I_2_D;
            case 0x88:
                return L_2_I;
            case 0x89:
                return L_2_F;
            case 0x8a:
                return L_2_D;
            case 0x8b:
                return F_2_I;
            case 0x8c:
                return F_2_L;
            case 0x8d:
                return F_2_D;
            case 0x8e:
                return D_2_I;
            case 0x8f:
                return D_2_L;
            case 0x90:
                return D_2_F;
            case 0x91:
                return I_2_B;
            case 0x92:
                return I_2_C;
            case 0x93:
                return I_2_S;
            case 0x94:
                return L_CMP;
            case 0x95:
                return F_CMP_L;
            case 0x96:
                return F_CMP_G;
            case 0x97:
                return D_CMP_L;
            case 0x98:
                return D_CMP_G;
            case 0x99:
                return new IfEq();
            case 0x9a:
                return new IfNe();
            case 0x9b:
                return new IfLt();
            case 0x9c:
                return new IfGe();
            case 0x9d:
                return new IfGt();
            case 0x9e:
                return new IfLe();
            case 0x9f:
                return new IfICmpEq();
            case 0xa0:
                return new IfICmpNe();
            case 0xa1:
                return new IfICmpLt();
            case 0xa2:
                return new IfICmpGe();
            case 0xa3:
                return new IfICmpGt();
            case 0xa4:
                return new IfICmpLe();
            case 0xa5:
                return new IfACmpEq();
            case 0xa6:
                return new IfACmpNe();
            case 0xa7:
                return new Goto();
            // case 0xa8:
            // 	return &JSR{}
            // case 0xa9:
            // 	return &RET{}
            case 0xaa:
                return new TableSwitch();
            case 0xab:
                return new LookupSwitch();
             case 0xac:
             	return I_RETURN;
             case 0xad:
             	return L_RETURN;
             case 0xae:
             	return F_RETURN;
             case 0xaf:
             	return D_RETURN;
             case 0xb0:
             	return A_RETURN;
             case 0xb1:
             	return RETURN;
            case 0xb2:
                return new GetStatic();
            case 0xb3:
                return new PutStatic();
            case 0xb4:
                return new GetField();
            case 0xb5:
                return new PutField();
            case 0xb6:
                return new InvokeVirtual();
            case 0xb7:
                return new InvokeSpecial();
            case 0xb8:
             	return new InvokeStatic();
            case 0xb9:
             	return new InvokeInterface();
            // case 0xba:
            // 	return &INVOKE_DYNAMIC{}
            case 0xbb:
                return new New();
             case 0xbc:
             	return new NewArray();
             case 0xbd:
             	return new ANewArray();
             case 0xbe:
             	return new ArrayLength();
             case 0xbf:
             	return A_THROW;
            case 0xc0:
                return new CheckCast();
            case 0xc1:
                return new InstanceOf();
            // case 0xc2:
            // 	return monitorenter
            // case 0xc3:
            // 	return monitorexit
            case 0xc4:
                return new Wide();
             case 0xc5:
             	return new MultiANewArray();
            case 0xc6:
                return new IfNull();
            case 0xc7:
                return new IfNonNull();
            case 0xc8:
                return new GotoW();
            // case 0xc9:
            // 	return &JSR_W{}
            // case 0xca: breakpoint
            // case 0xfe: impdep1
            case 0xfe:
                return INVOKE_NATIVE;
            // case 0xff: impdep2
            default:
                throw new RuntimeException("Unsupported opcode: " + opcode + ", " + Integer.toHexString(opcode));
        }
    }

}